import autosize from 'autosize';
import {on, off, hasClass, setStyle, getStyle} from '../../utils/DomUtil';
import {DEF_TEXT_STYLE} from '../../enum/TextStyle';
import {merge} from '../../utils';

// @ts-ignore
const DragPan = ol.interaction.DragPan;

// @ts-ignore
export default class PlotTextBox extends ol.Overlay {
    // 地图交互
    private _mapDragPan: any;
    // 是否单击
    private _isClick: boolean = false;
    // 是否处于拖拽状态
    private _isDraggin: boolean = false;
    // 当前气泡是否获取焦点
    private _isFocus: boolean = false;
    // 当前配置信息
    private _options: any;
    // 当前气泡位置
    private _position: any;
    // 防抖延时
    private _handleTimer: any;
    // 每次鼠标按下的位置
    private _currentPixel: Array<number> = [];

    public isPlotText: boolean = true;

    private _map: any;

    private _preCursor: any;

    constructor(options: any = {}) {
        const [id, element, offset, stopEvent, positioning, insertFirst,
            autoPan, autoPanAnimation, autoPanMargin, className] = [
            options['id'], options['element'], options['offset'], options['stopEvent'],
            options['positioning'], options['insertFirst'], options['autoPan'],
            options['autoPanAnimation'], options['autoPanMargin'],
            (options['className'] ? options['className'] : 'ol-plot-text-editor')
        ];
        super({
            id,
            element,
            stopEvent,
            insertFirst,
            autoPan,
            autoPanAnimation,
            autoPanMargin,
            className
        });
        this._options = options;
        this._position = (options['position'] && options['position'].length > 0) ? options['position'] : [];
        // @ts-ignore
        this.setOffset(offset !== undefined ? offset : [0, 0]);
        // @ts-ignore
        this.setPositioning(positioning !== undefined ? positioning : 'center-center');

        this.createTextContent(options);
    }

    /**
     * 创建文本框父容器
     * @param options
     * @private
     */
    private createTextContent(options: any): void {
        const content: HTMLElement = document.createElement('textarea');
        content.className = options.className || 'ol-plot-text-editor';
        content.style.width = options.style.width;
        content.style.height = options.style.height;
        content.style.minHeight = options.minHeight || '10px';
        content.setAttribute('id', options.id);
        content.setAttribute('autofocus', 'true');
        autosize(content);
        if (on) {
            on(content, 'focus', this.focusHandler);
            on(content, 'blur', this.blurHandler);
            on(content, 'click', this.clickHandler);
            on(content, 'mousedown', this.dragStartHandler);
            on(window, 'mouseup', this.dragEndHandler);
        }
        // @ts-ignore
        this.setElement(content);
        this.createCloseButton(options);
        this.createResizeButton(options);
        // @ts-ignore
        this.setPosition(this._position);
        // @ts-ignore
        this.dispatchEvent('textBoxDrawEnd', {
            overlay: this,
            element: content,
            uuid: options['id']
        });
    }

    /**
     * 获取文本框
     * @private
     */
    private getTextAreaFromContent(): any {
        let _node: any = '';
        // @ts-ignore
        const children: Array<any> = Array.prototype.slice.call((this.element && this.element.children), 0);
        if (children.length > 0) {
            children.every((element: HTMLElement): boolean => {
                if (element.nodeType === 1 && element.nodeName.toLowerCase() === 'textarea') {
                    _node = element;
                    return false;
                } else {
                    return true;
                }
            });
        }
        return _node;
    }

    /**
     * 创建关闭按钮
     * @param options
     * @private
     */
    private createCloseButton(options: any): void {
        const closeSpan: HTMLElement = document.createElement('span');
        closeSpan.className = 'ol-plot-text-editor-close';
        closeSpan.setAttribute('data-id', options['id']);
        if (off && on) {
            off(closeSpan, 'click', this.closeCurrentPlotText);
            on(closeSpan, 'click', this.closeCurrentPlotText);
            // @ts-ignore
            (this.element as HTMLElement).appendChild(closeSpan);
        }
    }

    /**
     * 处理关闭事件
     * @param event
     */
    private closeCurrentPlotText = (event: any): void => {
        if (!this._map) return;
        if (event && hasClass(event.target, 'ol-plot-text-editor-close')) {
            const id: string = event.target.getAttribute('data-id');
            if (id) {
                const overlay: any = this._map.getOverlayById(id);
                if (overlay) {
                    this._map.removeOverlay(overlay);
                }
            }
        }
    };

    /**
     * 创建文本框大小调整按钮
     * @param options
     * @private
     */
    private createResizeButton(options: any): void {
        const resizeSpan: HTMLElement = document.createElement('span');
        resizeSpan.className = 'ol-plot-text-editor-resize';
        resizeSpan.setAttribute('data-id', options['id']);
        if (on && off) {
            off(resizeSpan, 'mousedown', this.resizeMouseDowHandler);
            off(resizeSpan, 'mousemove', this.resizeMouseMoveHandler);
            on(resizeSpan, 'mousedown', this.resizeMouseDowHandler);
            on(resizeSpan, 'mousemove', this.resizeMouseMoveHandler);
            // @ts-ignore
            (this.element as HTMLElement).appendChild(resizeSpan);
        }
    }

    /**
     * 调整大小
     * @param event
     */
    private resizeButtonMoveHandler = (event: any): any => {
        const pixel: Array<number> = event.pixel;
        const element: HTMLElement = this.getTextAreaFromContent();
        if (pixel.length < 1 || this._currentPixel.length < 1 || !element) return;
        const offset: Array<number> = [pixel[0] - this._currentPixel[0], pixel[1] - this._currentPixel[1]];
        const size: Array<number> = [element.offsetWidth, element.offsetHeight];
        const width: number = size[0] + offset[0] * 2;
        const height: number = size[1] + offset[1] * 2;
        setStyle(element, 'width', `${width}px`);
        setStyle(element, 'height', `${height}px`);
        this._currentPixel = pixel;
        this._map.render();
    };

    /**
     * 处理鼠标按下事件
     * @param event
     */
    private resizeMouseDowHandler = (event: any): void => {
        if (!this._map) return;
        this._currentPixel = [event.x, event.y];
        this._map.on('pointermove', this.resizeButtonMoveHandler);
        if (on) {
            on(this._map.getViewport(), 'mouseup', this.resizeMouseUpHandler);
        }
    };

    /**
     * 处理鼠标抬起事件，移除所有事件监听
     * @param event
     */
    private resizeMouseUpHandler = (event: any): void => {
        this._map.un('pointermove', this.resizeButtonMoveHandler);
        if (off) {
            off(this._map.getViewport(), 'mouseup', this.resizeMouseUpHandler);
        }
        this._currentPixel = [];
    };

    /**
     * 处理移动事件
     * @param event
     */
    private resizeMouseMoveHandler = (event: Event): void => {
        event.stopImmediatePropagation();
    };

    /**
     * 处理获取焦点事件
     * @param event
     */
    private focusHandler = (event: any): void => {
        this._isFocus = true;
        if (this._map) {
            this._map.set('activeTextArea', this);
            this._map.dispatchEvent('activeTextArea');
        }
    };

    /**
     * 处理失去焦点事件
     * @param event
     */
    private blurHandler = (event: any): void => {
        this._isFocus = false;
        if (this._map) {
            this._map.set('activeTextArea', null);
            this._map.set('disActiveTextArea', this);
            this._map.dispatchEvent('disActiveTextArea');
        }
    };

    /**
     * 处理点击事件
     * @param event
     */
    private clickHandler = (event: any): | void => {
        // @ts-ignore
        this._isClick = event.target === this.element;
    };

    /**
     * 处理拖拽开始
     * @param event
     */
    private dragStartHandler = (event: any): void => {
        if (!this._map) return;
        if (!this._isDraggin && this.isMoveModel() && this._isFocus) {
            this._handleTimer = window.setTimeout((): void => {
                window.clearTimeout(this._handleTimer);
                this._handleTimer = null;
                if (!this._isClick) {
                    this._isDraggin = true;
                    this.disableMapDragPan();
                    // @ts-ignore
                    this._preCursor = this.element.style.cursor;
                    if (on) {
                        on(this._map.getViewport(), 'mousemove', this.draggingHandler);
                        // @ts-ignore
                        on(this.element, 'mouseup', this.dragEndHandler);
                    }
                }
            }, 300);
        }
    };

    /**
     * 处理拖拽中事件
     * @param event
     */
    private draggingHandler = (event: MouseEvent): void => {
        if (this._isDraggin) {
            // @ts-ignore
            this.element.style.cursor = 'move';
            this._position = this._map.getCoordinateFromPixel([event.clientX, event.clientY]);
            // @ts-ignore
            this.setPosition(this._position);
        }
    };

    /**
     * 处理拖拽结束
     * @param event
     */
    private dragEndHandler = (event: any): void => {
        this._isClick = false;
        window.clearTimeout(this._handleTimer);
        this._handleTimer = null;
        if (this._isDraggin && this._isFocus) {
            this._isDraggin = false;
            this.enableMapDragPan();
            // @ts-ignore
            this.element.style.cursor = this._preCursor;
            if (off) {
                off(this._map.getViewport(), 'mousemove', this.draggingHandler);
                // @ts-ignore
                off(this.element, 'mouseup', this.dragEndHandler);
            }
        }
    };

    /**
     * 是否处于选择模式
     */
    private isMoveModel = (): boolean | undefined => {
        return window.getSelection()?.getRangeAt(0).collapsed;
    };

    /**
     * 设置样式
     * @param style
     */
    private setStyle = (style: any = {}): void => {
        const element: HTMLElement = this.getTextAreaFromContent() as HTMLElement;
        if (element) {
            for (let key in style) {
                if (style[key]) {
                    setStyle(element, key, style[key]);
                }
            }
        }
    };

    /**
     * 获取当前样式
     * @return {CSSStyleDeclaration}
     */
    private getStyle = (): any => {
        const style: any = {};
        const element: HTMLElement = this.getTextAreaFromContent() as HTMLElement;
        if (element) {
            for (let key in DEF_TEXT_STYLE) {
                style[key] = getStyle(element, key);
            }
        }
        return style;
    };

    /**
     * 设置value 值
     * @param value
     */
    private setValue = (value: any): void => {
        const element: any = this.getTextAreaFromContent();
        if (element) {
            element.value = value;
            if (value) {
                autosize.update(element);
            }
            this._map.render();
        }
    };

    /**
     * 获取value
     */
    private getValue = (): any => {
        const element: any = this.getTextAreaFromContent();
        if (element) {
            return element.value;
        } else {
            return '';
        }
    };

    /**
     * 获取宽度
     */
    private getWidth = (): number => {
        const element: any = this.getTextAreaFromContent();
        if (element && element.offsetWidth) {
            return element.offsetWidth;
        } else {
            return 0;
        }
    };

    /**
     * 获取高度
     */
    private getHeight = (): number => {
        const element: any = this.getTextAreaFromContent();
        if (element && element.offsetHeight) {
            return element.offsetHeight;
        } else {
            return 0;
        }
    };

    /**
     * 禁止地图的拖拽平移
     */
    private disableMapDragPan = (): void => {
        if (!this._map) return;
        const interactions: Array<any> = this._map.getInteractions().getArrow();
        interactions.every((item: any): boolean => {
            if (item instanceof DragPan) {
                this._mapDragPan = item;
                this._map.removeInteraction(item);
                return false;
            } else {
                return true;
            }
        });
    };

    /**
     * 激活地图的拖拽平移
     */
    private enableMapDragPan = (): void => {
        if (!this._map) return;
        if (this._mapDragPan && this._mapDragPan instanceof DragPan) {
            this._map.addInteraction(this._mapDragPan);
            delete this._mapDragPan;
        }
    };

    private setMap(map: any): void {
        // @ts-ignore
        ol.Overlay.prototype.setMap.call(this, map);
        this._map = map;
        // @ts-ignore
        if (map && map instanceof ol.Map) {
            this.setStyle(merge(DEF_TEXT_STYLE, this._options['style']));
            this.setValue(this._options['value']);
        }
    }
}